﻿//---------------------------------------------------------------------
// Author: Reinhard Lehrbaum
//
// Description: Active Directory provider.
//
// Creation Date: 2006-12-30
//---------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.DirectoryServices;
using System.IO;
using System.Management.Automation;
using System.Management.Automation.Provider;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
using ActiveDs;

namespace Pscx.Providers
{
    [CmdletProvider("ActiveDirectory", ProviderCapabilities.ShouldProcess)]
    public class ActiveDirectoryPovider : NavigationCmdletProvider, IPropertyCmdletProvider
    {
        public enum DirectoryEntryClass
        {
            Unkown,
            User,
            Group,
            Contact,
            Computer,
            OrganizationalUnit,
            Volume,
            Printer,
            msMQRecipient,
            inetOrgPerson
        }

        private string _pathSeparator = System.IO.Path.DirectorySeparatorChar.ToString();
        private string _separator = "/";
        private bool _advancedView = false; //Show user, group and computer as container
        private bool _showAdditionalProperty = true;

        #region DriveCmdletProvider Overrides

        protected override PSDriveInfo NewDrive(PSDriveInfo drive)
        {
            if (drive == null)
            {
                ThrowTerminatingError(new ErrorRecord(new ArgumentNullException("drive"), "NullDrive", ErrorCategory.InvalidArgument, null));
            }

            DirectoryEntry RootEntry = null;
            try
            {
                string Root = String.IsNullOrEmpty(drive.Root) ? "LDAP://RootDSE" : drive.Root;

                RootEntry = GetDirectoryEntry(Root);
                string RootName = RootEntry.Path;
            }
            catch (Exception ex)
            {
                ThrowTerminatingError(new ErrorRecord(ex, "NoRoot", ErrorCategory.InvalidArgument, drive));
            }

            string RootDN = "";
            string RootCN = "";
            if (string.IsNullOrEmpty(drive.Root))
            {
                RootDN = RootEntry.Properties["defaultNamingContext"].Value.ToString();
            }
            else
            {
                RootDN = RootEntry.Properties["distinguishedName"].Value.ToString();
            }
            RootCN = ConvertDistinguishedToCanonicalName(RootDN);
            if (!RootCN.EndsWith(_separator))
            {
                RootCN += _separator;
            }

            PSDriveInfo driveEx = new PSDriveInfo(drive.Name, drive.Provider, RootCN, drive.Description, drive.Credential);
            ActiveDirectoryDriveInfo ADDrive = new ActiveDirectoryDriveInfo(driveEx);
            ADDrive.RootDN = RootDN;
            ADDrive.RootCN = RootCN;

            return ADDrive;
        }

        #endregion DriveCmdletProvider Overrides

        #region ItemCmdletProvider Overrides

        protected override bool IsValidPath(string path)
        {
            bool result = true;

            foreach (string pathChunk in ChunkPath(NormalizePath(path)))
            {
                if (pathChunk.Length == 0)
                {
                    result = false;
                    break;
                }
            }

            return result;
        }

        protected override bool ItemExists(string path)
        {
            try
            {
                GetDirectoryEntryFromPSPath(path);
            }
            catch
            {
                return false;
            }

            return true;
        }

        protected override void GetItem(string path)
        {
            DirectoryEntry item = GetDirectoryEntryFromPSPath(path);

            WriteEntry(item, path, IsContainer(item));
        }

        #endregion ItemCmdletProvider Overrides

        #region ContainerCmdletProvider Overrides

        protected override void GetChildItems(string path, bool recurse)
        {
            DirectoryEntry RootItem = GetDirectoryEntryFromPSPath(path);

            GetChildItems(RootItem, recurse);
        }

        protected override void GetChildNames(string path, ReturnContainers returnAllContainers)
        {
            DirectoryEntry RootItem = GetDirectoryEntryFromPSPath(path);

            foreach (DirectoryEntry item in RootItem.Children)
            {
                bool Container = IsContainer(item);
                WriteItemObject(item.Properties["Name"].Value.ToString(), path, Container);
            }
        }

        protected override void RenameItem(string path, string newName)
        {
            DirectoryEntry item = GetDirectoryEntryFromPSPath(path);

            if (NormalizePathAndRemoveRoot(path) == "")
            {
                ThrowTerminatingError(new ErrorRecord(new Exception("Root renaming is not supported"), "", ErrorCategory.InvalidOperation, path));
            }

            if (ShouldProcess(path, "rename"))
            {
                string dn = item.Properties["distinguishedName"].Value.ToString();
                dn = Regex.Replace(dn, "^([^=]*=).*$", "$1", RegexOptions.IgnoreCase);
                newName = dn + newName;

                item.Rename(newName);
                if (item.UsePropertyCache)
                {
                    item.CommitChanges();
                }
                WriteEntry(item, path, false);
            }

        }

        protected override void NewItem(string path, string type, object newItemValue)
        {
            if (string.IsNullOrEmpty(type))
            {
                ThrowTerminatingError(new ErrorRecord(new Exception("Missing an argument for parameter 'ItemType'. Specify a parameter of type 'System.String' and try again."), "Type", ErrorCategory.InvalidType, type));
            }
            DirectoryEntryClass ObjectType = GetItemType(type);
            if (ObjectType == DirectoryEntryClass.Unkown)
            {
                ThrowTerminatingError(new ErrorRecord(new Exception("The type is not a known type for the directory service. " +
                    "Only \"User\", \"Group\", \"Contact\", \"Computer\", \"OrganizationalUnit\", \"Volume\", \"msMQRecipient\" and \"inetOrgPerson\" can be specified."), "Type", ErrorCategory.InvalidType, type));
            }

            if (ObjectType == DirectoryEntryClass.Printer)
            {
                ThrowTerminatingError(new ErrorRecord(new Exception("Type not implemented."), "", ErrorCategory.NotImplemented, type));
            }

            string ParentPath = NormalizePath(path);
            string NewName = ParentPath.Substring(ParentPath.LastIndexOf(_separator) + 1);
            ParentPath = ParentPath.Substring(0, ParentPath.LastIndexOf(_separator) + 1);

            DirectoryEntry parent = GetDirectoryEntryFromPSPath(ParentPath);

            if (ShouldProcess(path, "new item"))
            {
                DirectoryEntry NewItem = parent.Children.Add((ObjectType != DirectoryEntryClass.OrganizationalUnit ? "CN=" : "OU=") + NewName, ItemTypeToString(ObjectType));
                switch (ObjectType)
                {
                    case DirectoryEntryClass.inetOrgPerson:
                    case DirectoryEntryClass.User:
                        NewItem.Properties["sAMAccountName"].Add(NewName);
                        NewItem.Properties["userPrincipalName"].Add(NewName + "@" + GetDomainName());
                        break;
                    case DirectoryEntryClass.Group:
                        NewItem.Properties["sAMAccountName"].Add(NewName);
                        if (newItemValue != null)
                        {
                            if (newItemValue.GetType() == typeof(string))
                            {
                                bool error = true;
                                string value = ((string)newItemValue).ToLower();
                                if (value.Length == 2)
                                {
                                    int GroupType = 0;

                                    bool GroupScopeUsed = false;
                                    bool GroupTypeUsed = false;

                                    error = false;

                                    foreach (char c in value.ToCharArray())
                                    {
                                        switch (c)
                                        {
                                            case 'l': // Domain Local 
                                                error = GroupScopeUsed ? true : error;
                                                GroupType = GroupType | (int)ADS_GROUP_TYPE_ENUM.ADS_GROUP_TYPE_LOCAL_GROUP;
                                                GroupScopeUsed = true;
                                                break;
                                            case 'g': // Global 
                                                error = GroupScopeUsed ? true : error;
                                                GroupType = GroupType | (int)ADS_GROUP_TYPE_ENUM.ADS_GROUP_TYPE_GLOBAL_GROUP;
                                                GroupScopeUsed = true;
                                                break;
                                            case 'u': // Universal
                                                error = GroupScopeUsed ? true : error;
                                                GroupType = GroupType | (int)ADS_GROUP_TYPE_ENUM.ADS_GROUP_TYPE_UNIVERSAL_GROUP;
                                                GroupScopeUsed = true;
                                                break;
                                            case 's': // Security
                                                error = GroupTypeUsed ? true : error;
                                                GroupType = GroupType | (int)ADS_GROUP_TYPE_ENUM.ADS_GROUP_TYPE_SECURITY_ENABLED;
                                                GroupTypeUsed = true;
                                                break;
                                            case 'd': // Distribution
                                                error = GroupTypeUsed ? true : error;
                                                GroupTypeUsed = true;
                                                break;
                                            default:
                                                error = true;
                                                break;
                                        }
                                    }
                                    if (!error && GroupType != 0)
                                    {
                                        NewItem.Properties["groupType"].Value = GroupType;
                                    }
                                    else
                                    {
                                        error = true;
                                    }
                                }
                                if (error)
                                {
                                    ThrowTerminatingError(new ErrorRecord(
                                        new Exception("Group type and scope combination not supported. " +
                                        "Only \"L\" (Domain Local Group), \"G\" (Global Group) or \"U\" (Universal Group) in combination with \"S\" (Security Group) or \"D\" (Distribution) can be specified."),
                                        "Group type", ErrorCategory.InvalidType, newItemValue));
                                }

                            }
                            else
                            {
                                ThrowTerminatingError(new ErrorRecord(
                                        new Exception("Group type and scope combination not supported. " +
                                        "Only \"L\" (Domain Local Group), \"G\" (Global Group) or \"U\" (Universal Group) in combination with \"S\" (Security Group) or \"D\" (Distribution) can be specified."),
                                        "Group type", ErrorCategory.InvalidType, newItemValue));
                            }
                        }
                        break;
                    case DirectoryEntryClass.Contact:
                        //OK
                        break;
                    case DirectoryEntryClass.Computer:
                        NewItem.Properties["sAMAccountName"].Add(NewName + "$");
                        break;
                    case DirectoryEntryClass.OrganizationalUnit:
                        //OK
                        break;
                    case DirectoryEntryClass.Volume:
                        if (newItemValue != null && newItemValue.GetType() == typeof(string))
                        {
                            NewItem.Properties["uNCName"].Add(newItemValue);
                        }
                        else
                        {
                            WriteError(new ErrorRecord(
                                new Exception("New-Item : Cannot process argument because the value of argument \"value\" is invalid. Change the value of the \"value\" argument (network path, UNC e.g. \"\\\\server\\share\") and run the operation again."),
                                "Share", ErrorCategory.InvalidArgument, newItemValue));
                            return;
                        }
                        break;
                    case DirectoryEntryClass.Printer:
                        //not implemented
                        break;
                    case DirectoryEntryClass.msMQRecipient:
                        //OK
                        break;
                    default:
                        break;
                }

                NewItem.CommitChanges();

                WriteEntry(NewItem, path, false);
            }
        }

        protected override void RemoveItem(string path, bool recurse)
        {
            DirectoryEntry item = GetDirectoryEntryFromPSPath(path);

            if (ShouldProcess(path, "delete"))
            {
                item.DeleteTree();
            }
        }

        protected override bool HasChildItems(string path)
        {
            DirectoryEntry item = GetDirectoryEntryFromPSPath(path);

            foreach (DirectoryEntry child in item.Children)
            {
                return true;
            }
            return false;
        }

        #endregion ContainerCmdletProvider Overrides

        #region NavigationCmdletProvider Overrides

        protected override string MakePath(string parent, string child)
        {
            return NormalizePath(base.MakePath(parent, child)); // return combined path
        }

        protected override string GetParentPath(string path, string root)
        {
            if (string.IsNullOrEmpty(path) && string.IsNullOrEmpty(root))
            {
                return string.Empty;
            }
            else
            {
                return NormalizePath(base.GetParentPath(path, root)); // return parentpath
            }
        }

        protected override string GetChildName(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return string.Empty;
            }
            else
            {
                return base.GetChildName(path); // return childname
            }
        }

        protected override bool IsItemContainer(string path)
        {
            DirectoryEntry RootItem = GetDirectoryEntryFromPSPath(path);

            return IsContainer(RootItem);
        }

        protected override void MoveItem(string path, string destination)
        {
            DirectoryEntry item = GetDirectoryEntryFromPSPath(path);
            DirectoryEntry Target = GetDirectoryEntryFromPSPath(destination);

            if (ShouldProcess(path, "move"))
            {
                item.MoveTo(Target);
            }
        }

        protected override string NormalizeRelativePath(string path, string basePath)
        {
            return base.NormalizeRelativePath(path, basePath);
        }

        #endregion NavigationCmdletProvider Overrides

        #region IPropertyCmdletProvider Overrides

        public void GetProperty(string path, Collection<string> providerSpecificPickList)
        {
            PSObject psObject = new PSObject();

            DirectoryEntry item = GetDirectoryEntryFromPSPath(path);
            DirectoryEntryProperty EntryProperty = new DirectoryEntryProperty(item);

            bool ShowAll = false;
            RuntimeDefinedParameterDictionary dic = DynamicParameters as RuntimeDefinedParameterDictionary;
            if (dic != null && dic.ContainsKey("ShowAll"))
            {
                RuntimeDefinedParameter rdp = dic["ShowAll"];
                ShowAll = rdp.IsSet;
            }
            EntryProperty.ShowAllProperties = ShowAll;
            EntryProperty.UseAdditionalProperties = _showAdditionalProperty;

            ArrayList propList = new ArrayList();

            if (providerSpecificPickList == null || providerSpecificPickList.Count == 0)
            {
                propList = EntryProperty.GetPropertyNameList();
            }
            else
            {
                propList.AddRange(providerSpecificPickList);
            }

            foreach (string property in propList)
            {
                try
                {
                    object value = EntryProperty[property];
                    if ((value != null && !string.IsNullOrEmpty(value.ToString())) || ShowAll)
                    {
                        psObject.Properties.Add(new PSNoteProperty(property, value));
                    }
                }
                catch (Exception ex)
                {
                    WriteError(new ErrorRecord(ex, "PropertyError", ErrorCategory.InvalidData, property));
                }
            }

            WritePropertyObject(psObject, path);
        }

        public object GetPropertyDynamicParameters(string path, Collection<string> providerSpecificPickList)
        {
            ParameterAttribute ShowAllAttrib = new ParameterAttribute();
            ShowAllAttrib.Mandatory = false;
            ShowAllAttrib.ValueFromPipeline = false;

            RuntimeDefinedParameter ShowAllParam = new RuntimeDefinedParameter();
            ShowAllParam.IsSet = false;
            ShowAllParam.Name = "ShowAll";
            ShowAllParam.ParameterType = typeof(SwitchParameter);
            ShowAllParam.Attributes.Add(ShowAllAttrib);

            RuntimeDefinedParameterDictionary dic = new RuntimeDefinedParameterDictionary();
            dic.Add("ShowAll", ShowAllParam);

            return dic;
        }

        public void SetProperty(string path, PSObject propertyValue)
        {
            DirectoryEntry item = GetDirectoryEntryFromPSPath(path);
            if (ShouldProcess(path, "set property"))
            {
                foreach (PSPropertyInfo info in propertyValue.Properties)
                {
                    DirectoryEntryProperty EntryProperty = new DirectoryEntryProperty(item);

                    RuntimeDefinedParameterDictionary dic = DynamicParameters as RuntimeDefinedParameterDictionary;
                    if (dic != null)
                    {
                        bool AddPropertyValue = false;
                        bool RemovePropertyValue = false;

                        if (dic.ContainsKey("Add"))
                        {
                            RuntimeDefinedParameter rdp = dic["Add"];
                            AddPropertyValue = rdp.IsSet;
                        }
                        if (dic.ContainsKey("Remove"))
                        {
                            RuntimeDefinedParameter rdp = dic["Remove"];
                            RemovePropertyValue = rdp.IsSet;
                        }

                        if (AddPropertyValue && RemovePropertyValue)
                        {
                            AddPropertyValue = false;
                            RemovePropertyValue = false;
                        }

                        if (AddPropertyValue)
                        {
                            EntryProperty.WriteType = DirectoryEntryProperty.PropertyWrite.Add;
                        }
                        else if (RemovePropertyValue)
                        {
                            EntryProperty.WriteType = DirectoryEntryProperty.PropertyWrite.Remove;
                        }
                    }
                    if (this.Force)
                    {
                        EntryProperty.WriteType = DirectoryEntryProperty.PropertyWrite.Invoke;
                    }

                    EntryProperty[info.Name] = info.Value;
                }

                WritePropertyObject(propertyValue, path);
            }
        }

        public object SetPropertyDynamicParameters(string path, PSObject propertyValue)
        {
            RuntimeDefinedParameterDictionary dic = new RuntimeDefinedParameterDictionary();

            ParameterAttribute AddAttrib = new ParameterAttribute();
            AddAttrib.Mandatory = false;
            AddAttrib.ValueFromPipeline = false;
            //AddAttrib.ParameterSetName = "AddValue";

            RuntimeDefinedParameter ParamAdd = new RuntimeDefinedParameter();
            ParamAdd.IsSet = false;
            ParamAdd.Name = "Add";
            ParamAdd.ParameterType = typeof(SwitchParameter);
            ParamAdd.Attributes.Add(AddAttrib);

            dic.Add("Add", ParamAdd);

            ParameterAttribute DelAttrib = new ParameterAttribute();
            DelAttrib.Mandatory = false;
            DelAttrib.ValueFromPipeline = false;
            //DelAttrib.ParameterSetName = "RemoveValue";

            RuntimeDefinedParameter ParamDel = new RuntimeDefinedParameter();
            ParamDel.IsSet = false;
            ParamDel.Name = "Remove";
            ParamDel.ParameterType = typeof(SwitchParameter);
            ParamDel.Attributes.Add(DelAttrib);

            dic.Add("Remove", ParamDel);

            return dic;
        }

        public void ClearProperty(string path, Collection<string> propertyToClear)
        {
            if (ShouldProcess(path, "clear properties"))
            {
                using (DirectoryEntry item = GetDirectoryEntryFromPSPath(path))
                {
                    DirectoryEntryProperty EntryProperty = new DirectoryEntryProperty(item);

                    foreach (string propertyName in propertyToClear)
                    {
                        EntryProperty.ClearProperty(propertyName);

                        WritePropertyObject(propertyName, path);
                    }
                }
            }
        }

        public object ClearPropertyDynamicParameters(string path, Collection<string> propertyToClear)
        {
            return null;
        }

        #endregion IPropertyCmdletProvider

        #region Helper Methods

        private string[] ChunkPath(string path)
        {
            string pathNoDrive = NormalizePathAndRemoveRoot(path);

            return pathNoDrive.Split(_separator.ToCharArray());
        }

        private string NormalizePath(string path)
        {
            string result = path;

            if (!String.IsNullOrEmpty(path))
            {
                result = path.Replace(_pathSeparator, _separator);
            }

            return result;
        }

        private string NormalizePathAndRemoveRoot(string path)
        {
            string result = NormalizePath(path);

            if (result.Length < this.PSDriveInfo.Root.Length)
            {
                result += _separator;
            }

            result = Regex.Replace(result, "^" + this.PSDriveInfo.Root, "", RegexOptions.IgnoreCase);
            if (result.StartsWith(_separator))
            {
                result = result.Substring(1);
            }
            return result;
        }

        private DirectoryEntry GetDirectoryEntry(string Path)
        {
            DirectoryEntry result = new DirectoryEntry(Path);
            if (!string.IsNullOrEmpty(this.Credential.UserName))
            {
                result.Username = this.Credential.UserName;
                result.Password = this.Credential.GetNetworkCredential().Password;
            }
            return result;
        }

        private DirectoryEntry GetDirectoryEntryFromPSPath(string PSPath)
        {
            string normPath = NormalizePathAndRemoveRoot(PSPath);

            ActiveDirectoryDriveInfo drive = (ActiveDirectoryDriveInfo)this.PSDriveInfo;
            return GetDirectoryEntry(ExtendPath(ConvertCanonicalToDistinguishedName(drive.RootCN + normPath)));
        }

        private string ExtendPath(string Path)
        {
            string result = Path;

            int p = Path.IndexOf("://");
            if (p < 0)
            {
                result = "LDAP://" + Path;
            }
            else
            {
                result = Path.Substring(0, p).ToUpper() + Path.Substring(p + 1);
            }
            return result;
        }

        private string ConvertDistinguishedToCanonicalName(string valueDN)
        {
            NameTranslateClass Translator = new NameTranslateClass();

            Translator.Init((int)ADS_NAME_INITTYPE_ENUM.ADS_NAME_INITTYPE_GC, "");
            Translator.Set((int)ADS_NAME_TYPE_ENUM.ADS_NAME_TYPE_1779, valueDN);

            return Translator.Get((int)ADS_NAME_TYPE_ENUM.ADS_NAME_TYPE_CANONICAL);
        }

        private string ConvertCanonicalToDistinguishedName(string valueCN)
        {
            string value = (valueCN.EndsWith(_separator) && (valueCN.IndexOf(_separator) != valueCN.LastIndexOf(_separator)) ? valueCN.Substring(0, valueCN.Length - 1) : valueCN);

            NameTranslateClass Translator = new NameTranslateClass();

            Translator.Init((int)ADS_NAME_INITTYPE_ENUM.ADS_NAME_INITTYPE_GC, "");
            Translator.Set((int)ADS_NAME_TYPE_ENUM.ADS_NAME_TYPE_CANONICAL, value);

            return Translator.Get((int)ADS_NAME_TYPE_ENUM.ADS_NAME_TYPE_1779);
        }

        private bool IsContainer(DirectoryEntry entry)
        {
            ArrayList ClassList = new ArrayList();

            if (entry.Properties["objectclass"].Value.GetType() == typeof(object[]))
            {
                ClassList.AddRange((object[])entry.Properties["objectclass"].Value);
            }
            else
            {
                ClassList.Add(entry.Properties["objectclass"].Value);
            }
            foreach (string ClassItem in ClassList)
            {
                switch (ClassItem)
                {
                    case "domain":
                    case "organizationalUnit":
                    case "container":
                        return true;
                    case "user":
                    case "computer":
                    case "group":
                        if (_advancedView)
                        {
                            return true;
                        }
                        break;
                    default:
                        break;
                }
            }
            return false;
        }

        private DirectoryEntryClass GetEntryClass(DirectoryEntry entry)
        {
            ArrayList ClassList = new ArrayList();

            if (entry.Properties["objectclass"].Value.GetType() == typeof(object[]))
            {
                ClassList.AddRange((object[])entry.Properties["objectclass"].Value);
            }
            else
            {
                ClassList.Add(entry.Properties["objectclass"].Value);
            }

            bool maybeUser = false;

            foreach (string ClassItem in ClassList)
            {
                switch (ClassItem)
                //switch (ClassList[ClassList.Count - 1].ToString())
                {
                    case "organizationalUnit":
                        return DirectoryEntryClass.OrganizationalUnit;
                    case "user":
                        maybeUser = true;
                        break;
                    //return ObjectClass.User;
                    case "computer":
                        return DirectoryEntryClass.Computer;
                    case "group":
                        return DirectoryEntryClass.Group;
                    case "contact":
                        return DirectoryEntryClass.Contact;
                    case "msMQ-Custom-Recipient":
                        return DirectoryEntryClass.msMQRecipient;
                    case "volume":
                        return DirectoryEntryClass.Volume;
                    case "printQueue":
                        return DirectoryEntryClass.Printer;
                    default:
                        //return ObjectClass.Unkown;
                        break;
                }
            }

            if (maybeUser)
            {
                if (entry.Properties["objectCategory"].Value.ToString().StartsWith("CN=Person"))
                {
                    return DirectoryEntryClass.User;
                }
            }

            return DirectoryEntryClass.Unkown;
        }

        private string GetPath(DirectoryEntry entry)
        {
            string dn = entry.Properties["distinguishedName"].Value.ToString();
            return ConvertDistinguishedToCanonicalName(dn);
        }

        private void GetChildItems(DirectoryEntry parent, bool recurse)
        {
            string path = GetPath(parent);

            foreach (DirectoryEntry item in parent.Children)
            {
                bool Container = IsContainer(item);
                //WriteEntry(item, path + FSeparator + item.Properties["Name"].Value.ToString(), Container);
                WriteEntry(item, path, Container);

                if (recurse && Container)
                {
                    GetChildItems(item, recurse);
                }
            }
        }

        private DirectoryEntryClass GetItemType(string type)
        {
            DirectoryEntryClass result = DirectoryEntryClass.Unkown;

            type = type.ToLower();

            foreach (string ClassItem in Enum.GetNames(typeof(DirectoryEntryClass)))
            {
                if (ClassItem.Length >= type.Length)
                {
                    if (type.Equals(ClassItem.Substring(0, type.Length).ToLower()))
                    {
                        if (result != DirectoryEntryClass.Unkown)
                        {
                            //input type not well-defined 
                            result = DirectoryEntryClass.Unkown;
                            break;
                        }
                        result = (DirectoryEntryClass)Enum.Parse(typeof(DirectoryEntryClass), ClassItem, true);
                    }
                }
            }
            return result;
        }

        private string ItemTypeToString(DirectoryEntryClass type)
        {
            string result = "";
            switch (type)
            {
                case DirectoryEntryClass.User:
                    result = "user";
                    break;
                case DirectoryEntryClass.Group:
                    result = "group";
                    break;
                case DirectoryEntryClass.Contact:
                    result = "contact";
                    break;
                case DirectoryEntryClass.Computer:
                    result = "computer";
                    break;
                case DirectoryEntryClass.OrganizationalUnit:
                    result = "organizationalUnit";
                    break;
                case DirectoryEntryClass.Volume:
                    result = "volume";
                    break;
                case DirectoryEntryClass.Printer:
                    result = "";
                    break;
                case DirectoryEntryClass.msMQRecipient:
                    result = "msMQ-Custom-Recipient";
                    break;
                case DirectoryEntryClass.inetOrgPerson:
                    result = "inetOrgPerson";
                    break;
            }
            return result;
        }

        private string GetDomainName()
        {
            ActiveDirectoryDriveInfo drive = (ActiveDirectoryDriveInfo)this.PSDriveInfo;
            return drive.RootCN.Substring(0, drive.RootCN.IndexOf(_separator));
        }

        private void WriteEntry(DirectoryEntry item, string path, bool isContainer)
        {
            DirectoryEntryProperty entry = new DirectoryEntryProperty(item);
            WriteItemObject(entry.GetActiveDirectoryEntry(), path, isContainer);
        }

        #endregion Helper Methods
    }
}
